package com.dawn.seckilldemo.controller;

import cn.hutool.json.JSONUtil;
import com.dawn.seckilldemo.config.AccessLimit;
import com.dawn.seckilldemo.pojo.Order;
import com.dawn.seckilldemo.pojo.SeckillMessage;
import com.dawn.seckilldemo.pojo.SeckillOrder;
import com.dawn.seckilldemo.pojo.User;
import com.dawn.seckilldemo.rabbit.MQSenderMessage;
import com.dawn.seckilldemo.service.GoodsService;
import com.dawn.seckilldemo.service.OrderService;
import com.dawn.seckilldemo.service.SeckillOrderService;
import com.dawn.seckilldemo.vo.GoodsVo;
import com.dawn.seckilldemo.vo.RespBean;
import com.dawn.seckilldemo.vo.RespBeanEnum;
import com.ramostear.captcha.HappyCaptcha;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaStyle;
import com.ramostear.captcha.support.CaptchaType;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private SeckillOrderService seckillOrderService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MQSenderMessage mqSenderMessage;
    @Autowired
    private RedisScript<Long> script;

    private HashMap<Long,Boolean> entryStockMap=  new HashMap<>();


    //秒杀/{path}, @PathVariable String path
    @RequestMapping(value = "/doSeckill",method = RequestMethod.POST)
    public String doSeckill(Model model, User user, Long goodsId){
        if(user==null){
            return "login";
        }
        //路径判断
        /*boolean check = orderService.checkPath(user,goodsId,path);
        if(!check){
            model.addAttribute("errmsg",RespBeanEnum.REQUEST_ILLEGAL.getMessage());
            return "secKillFail";
        }*/
        //
       /* model.addAttribute("user",user);
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);

        //判断是否重复抢购,再redis中获取
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId())
//                .eq("goods_id", goodsId));
        /*if(seckillOrder !=null){
            model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }
        //从redis中取出数据，判断是否重复抢购


        //判断库存
        if(goodsVo.getStockCount()<1){
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }
        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsVo.getId());
        if(o !=null){
            model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }
        //抢购
        Order order =orderService.seckill(user,goodsVo);
        if (order == null){
            model.addAttribute("errmsg",RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }
        model.addAttribute("order",order);
        model.addAttribute("goods",goodsVo);
        return "orderDetail";*/

       //reids进行优化

        //判断是否重复抢购
        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if(o !=null){
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }
        //如果库存为空，避免再次请求reids，给redis增加负担（内存标记）
        if(entryStockMap.get(goodsId)){
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }
        //执行库存减一，获得减一后的库存 ----lua优化,判断和减库存一步操作
        Long execute = (Long) redisTemplate.execute(script, Collections.singletonList("seckillGoods:" + goodsId));
        //下单操作，使用rabbitmq进行
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
        if(execute<0){
            //这里使用内存标记，避免多次操作redis
            entryStockMap.put(goodsId,true);
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }

        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSenderMessage.senderMessage(JSONUtil.toJsonStr(seckillMessage));
        model.addAttribute("errmsg", "排队中。。。");
        return "secKillFail";
    }

    @RequestMapping(value = "/path",method = RequestMethod.GET)
    @ResponseBody
    @AccessLimit(second=5,maxCount=2,needLogin=true)
    public RespBean getPath(User user,Long goodsId,HttpServletRequest request){
        if(user==null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        //计数器redis,5秒内访问100，使用注解
        /*String uri = request.getRequestURI();
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Integer count = (Integer) valueOperations.get(uri + ":" + user.getId());
        if(count == null) {
            valueOperations.set(uri + ":" + user.getId(), 1, 5, TimeUnit.SECONDS);
        }else if(count <100){
            valueOperations.increment(uri + ":" + user.getId());
        }else {
            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
        }*/

        //创建真正的地址
        String url = orderService.createPath(user,goodsId);
        return RespBean.success(url);
    }

    @GetMapping("/result")
    public String getResult(Model model,User user,Long goodsId){
        if(user == null){
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }
        Long orderId = seckillOrderService.getResult(user,goodsId);
        if(orderId<0){
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }else if(orderId == 0){
            model.addAttribute("errmsg", "排队中，请稍后重试");
            return "secKillFail";
        }else {
            GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
            Order order = orderService.finbyOrderId(orderId);
            model.addAttribute("order",order);
             model.addAttribute("goods",goodsVo);
        return "orderDetail";
        }

    }


    //验证码放在session中的
    @GetMapping("/captcha")
    public void happyCaptcha(HttpServletRequest request, HttpServletResponse response){
        HappyCaptcha.require(request,response)
                .style(CaptchaStyle.ANIM)			//设置展现样式为动画
                .type(CaptchaType.NUMBER)			//设置验证码内容为数字
                .length(6)							//设置字符长度为6
                .width(220)							//设置动画宽度为220
                .height(80)							//设置动画高度为80
                .font(Fonts.getInstance().zhFont())	//设置汉字的字体
                .build().finish();      			//生成并输出验证码
    }
    //验证码校验
    @PostMapping("/verify")
    @ResponseBody
    public String verify(String code,HttpServletRequest request){
        //Verification Captcha
        boolean flag = HappyCaptcha.verification(request,code,true);
        if(!flag){
            HappyCaptcha.remove(request);
            return "222222";
        }
        else {
            return "11111";
        }
    }

    //验证码清除
    @GetMapping("/remove/captcha")
    public void removeCaptcha(HttpServletRequest request){
        HappyCaptcha.remove(request);
    }



    //bean初始化要执行的方法，把商品库存数量加载到redis中
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> list = goodsService.findGoodsVo();
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        //把所有的商品和库存数量存入reids中
        //key：value ===》 seckillGoods:goodsid : count
        list.forEach(goodsVo -> {
            redisTemplate.opsForValue().set("seckillGoods:"+goodsVo.getId(),goodsVo.getStockCount());
            //当有库存的是否为false，当无库存为true。防止库存没有了，还会进行判断操作
            entryStockMap.put(goodsVo.getId(),false);
        });

    }
}
